package main

import "fmt"

// --- 结构体 ---
// 结构体是字段的聚合，用于将不同类型的数据组合成一个实体。
// 类似于其他语言中的类，但没有继承。
type Person struct {
    Name string
    Age  int
    City string
}

// --- 自定义类型 ---
// 可以基于现有类型创建新类型，这有助于代码的可读性和类型安全。
type MyInt int

func main() {
    // --- 数组 ---
    // 数组是具有固定长度且元素类型相同的序列。
    // 数组的长度是其类型的一部分，因此 [3]int 和 [4]int 是不同的类型。
    var arr1 [5]int // 声明一个长度为5的整型数组，元素默认为零值
    fmt.Println("arr1 (零值):", arr1)

    arr2 := [5]int{1, 2, 3, 4, 5} // 声明并初始化
    fmt.Println("arr2:", arr2)

    arr3 := [...]int{10, 20, 30} // 让编译器根据初始值推断长度
    fmt.Println("arr3 (长度推断):", arr3)

    fmt.Println("arr2 的第一个元素:", arr2[0])
    arr2[0] = 100 // 修改元素
    fmt.Println("修改后的 arr2:", arr2)
    fmt.Println("arr2 的长度:", len(arr2))

    // --- 切片 ---
    // 切片是动态数组，它不存储任何数据，它只是对底层数组的一个视图。
    // 切片的长度可以改变，非常灵活。
    // 切片有三个属性：指针、长度和容量。
    var s1 []int // 声明一个 nil 切片
    fmt.Println("s1 (nil 切片):", s1, s1 == nil, len(s1), cap(s1))

    // 使用 make 创建切片，可以指定长度和容量
    s2 := make([]int, 3, 5) // 长度为3，容量为5
    fmt.Println("s2 (make创建):", s2, len(s2), cap(s2))

    // 切片的字面量
    s3 := []int{10, 20, 30, 40, 50}
    fmt.Println("s3 (字面量):", s3, len(s3), cap(s3))

    // 切片操作
    s3 = append(s3, 60) // 向切片追加元素，如果容量不足，会自动扩容
    fmt.Println("append 后的 s3:", s3, len(s3), cap(s3))

    // 切片的切片
    s4 := s3[1:4] // 创建一个从 s3[1] 到 s3[3] 的新切片
    fmt.Println("s3[1:4] 的结果:", s4)

    // --- Map ---
    // Map 是无序的键值对集合。键必须是唯一的。
    // 类似于其他语言中的字典或哈希表。
    var m1 map[string]int // 声明一个 nil map
    fmt.Println("m1 (nil map):", m1, m1 == nil)

    // 使用 make 创建 map
    m2 := make(map[string]int)
    m2["apple"] = 5
    m2["banana"] = 3
    fmt.Println("m2:", m2)

    // Map 的字面量
    m3 := map[string]string{
        "name": "张三",
        "city": "北京",
    }
    fmt.Println("m3:", m3)

    // 访问和修改 Map
    value, ok := m3["name"] // 两个返回值，第二个值表示键是否存在
    if ok {
        fmt.Println("m3中'name'的值是:", value)
    } else {
        fmt.Println("m3中不存在'name'这个键")
    }

    delete(m3, "city") // 删除键值对
    fmt.Println("删除'city'后的 m3:", m3)

    // --- 结构体 ---
    // 创建结构体实例
    p1 := Person{"李四", 30, "上海"}
    fmt.Println("p1:", p1)

    // 使用字段名初始化
    p2 := Person{
        Name: "王五",
        Age:  25,
        City: "广州",
    }
    fmt.Println("p2:", p2)

    // 访问结构体字段
    fmt.Printf("%s 今年 %d 岁，住在 %s\n", p2.Name, p2.Age, p2.City)

    // 修改字段值
    p2.Age = 26
    fmt.Println("修改年龄后的 p2:", p2)

    // 结构体指针
    p3 := &p2 // p3 是 p2 的指针
    (*p3).Name = "赵六" // 通过指针访问字段
    fmt.Println("通过指针修改后，p2 和 p3 的值:", p2, *p3)
    // Go 允许我们直接使用 p3.Name 来访问，它会自动解引用
    p3.City = "深圳"
    fmt.Println("直接使用指针访问并修改后:", p2)

    // --- 自定义类型 ---
    var a MyInt = 10
    var b int = 20
    // a = b // 编译错误！不能将不同类型的变量赋值
    a = MyInt(b) // 需要显式类型转换
    fmt.Println("自定义类型 MyInt 的值:", a)
}